package com.eece411.project.p2;

import java.net.InetAddress;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class Routing {
	private static final int MAX_CLIENT = 166; // Not 250 because planetlab is kill connectect
	private int clientCount;
	private int inChargeOf=-1;
	private ArrayList<Instance> instance = new ArrayList<Instance>(3);
	public static final int DATA_HEADER=1;
	public static final int DATA_LENGHT=1024;
	private Routing() {
		
		String url = "http://fdglec2.no-ip.org/eece411/nodes.xml";
		
		/*
		 * XML READER FROM http://theoryapp.com/parse-xml-using-dom-in-java/ 
		 * */
        try
        {
            DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
            DocumentBuilder b = f.newDocumentBuilder();
            Document doc = b.parse(url);
            doc.getDocumentElement().normalize();
            System.out.println ("Configuration ((nbCluster)x(nbNode)): " + doc.getDocumentElement().getNodeName());
            // loop through each item
            NodeList cluster = doc.getElementsByTagName("cluster");
            if(cluster.getLength()==0){
            	NodeList node = doc.getElementsByTagName("node");
                for (int i = 0; i < node.getLength(); i++)
                {
                	String[] parts = ((Element)node.item(i)).getAttribute("host").split(":");
                	if (InetAddress.getLocalHost().getHostName().equals(parts[0])){
                		instance.add(new DataStore(parts[0]));
    					System.out.println("------------------->Local : "+parts[0]+":"+parts[1]);
    					this.inChargeOf=i;
    				} else if (parts.length==2){
    					long start = System.currentTimeMillis();
    					InetAddress.getByName(parts[0]).isReachable(5000);
    					instance.add(new Node(parts[0],Integer.parseInt(parts[1]),System.currentTimeMillis()-start));
    					System.out.println("Remote : "+parts[0]+":"+parts[1]);
    				} else {
    					long start = System.currentTimeMillis();
    					InetAddress.getByName(parts[0]).isReachable(5000);
    					instance.add(new Node(parts[0],1234,System.currentTimeMillis()-start));
    					System.out.println("Remote : "+parts[0]+":1234");
    				}
                }
                if (node.getLength()==instance.size()){
                	System.out.println("All node from the xml are loaded. nbCluster : "+instance.size());
                } else {
                	System.out.println("Error in the xml loading!!!");
                }
            }
            for (int i = 0; i < cluster.getLength(); i++)
            {
                NodeList nodesList = ((Element) cluster.item(i)).getElementsByTagName("node");
                Instance[] nodes = new Instance[3];
        		System.out.println("Cluster : "+i);
                for(int j=0;j<3;j++){
                	String[] parts = ((Element)nodesList.item(j)).getAttribute("host").split(":");
                	if (InetAddress.getLocalHost().getHostName().equals(parts[0])){
                		nodes[j]=new DataStore(parts[0]);
                		System.out.println("------------------->Local : "+parts[0]+":"+parts[1]);
    					this.inChargeOf=i;
    				} else if (parts.length==2){
    					long start = System.currentTimeMillis();
    					InetAddress.getByName(parts[0]).isReachable(5000);
    					nodes[j] = new Node(parts[0],Integer.parseInt(parts[1]),System.currentTimeMillis()-start);
    					System.out.println("Remote : "+parts[0]+":"+parts[1]);
    				} else {
    					long start = System.currentTimeMillis();
    					InetAddress.getByName(parts[0]).isReachable(5000);
    					nodes[j] = new Node(parts[0],1234,System.currentTimeMillis()-start);
    					System.out.println("Remote : "+parts[0]+":1234");
    				}
                }
                instance.add(new Cluster(nodes[0],nodes[1],nodes[2]));
            }
            if (cluster.getLength()==instance.size()){
            	System.out.println("All node from the xml are loaded. nbCluster : "+instance.size());
            } else {
            	System.out.println("Error in the xml loading!!!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
		/*
		 * XML READER FROM http://theoryapp.com/parse-xml-using-dom-in-java/
		 * */
	}
	private static class SingletonHolder { 
		public static final Routing INSTANCE = new Routing();
	}
	// The method to get the routing info
	public static Routing getInstance() {
		return SingletonHolder.INSTANCE;
	}
	
	//put -> put in the Instance if ok return true
	public boolean put(byte[] key,byte[] data){
		try {
			return this.instance.get(this.responsableInstance(key)).put(key, data, false);
		} catch (deadException e) {
			return false;
		}
	}
	//get -> get in the Instance if ok return data
	public byte[] get(byte[] key){
		try {
			return this.instance.get(this.responsableInstance(key)).get(key);
		} catch (deadException e) {
			return null;
		}
	}
	// put -> remove in the Instance if ok return true
	public boolean remove(byte[] key){
		try {
			return this.instance.get(this.responsableInstance(key)).remove(key, false);
		} catch (deadException e) {
			return false;
		}
	}
	// return if exist in Instance
	public boolean containsKey(byte[] key){
		try {
			return this.instance.get(this.responsableInstance(key)).containsKey(key);
		} catch (deadException e) {
			return false;
		}
	}
	
	// Replication put async
	public void putSync(byte[] key,byte[] data){
		try {
			this.instance.get(this.inChargeOf).put(key, data, true);
		} catch (deadException e) {

		}
	}
	// Replication remove async
	public void removeSync(byte[] key){
		try {
			this.instance.get(this.inChargeOf).remove(key, true);
		} catch (deadException e) {

		}
	}
	
	// Return the responsable Instance of a key
	private int responsableInstance(byte[] key){
		return Math.abs(((new String(key)).hashCode()))%instance.size();
	}
	
	// Maxclient handling
	public synchronized boolean connectClient(){
		if(clientCount<MAX_CLIENT){
			clientCount++;
			return true;
		} else {
			return false;
		}
	}
	public synchronized void disconnectClient(){
		clientCount--;
	}
	public int getclientCount(){
		return clientCount;
	}
}
